package com.sc.weixin.order;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.xml.sax.InputSource;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sc.dal.model.order.Order;
import com.sc.dal.model.order.OrderPayment;
import com.sc.service.order.OrderService;
import com.sc.util.adapter.ConfigBeanAdapter;
import com.sc.util.data.Common;
import com.sc.util.data.Constants;
import com.sc.util.data.DateUtil;
import com.sc.weixin.BaseController;
import com.sc.weixin.util.GetWxOrderno;
import com.sc.weixin.util.MD5;
import com.sc.weixin.util.RequestHandler;
import com.sc.weixin.util.WeChat;
import com.sc.weixin.util.http.HttpClientConnectionManager;

/**
 * @ClassName: WeiXinController
 * @Description: 微信支付
 * @author LeiJia
 * @date 2017年06月08日 上午08:59:26
 * @version V1.0
 */
@Controller
@RequestMapping(value="/wechat")
public class WeiXinController extends BaseController {

	
	private  final Logger logger = LoggerFactory.getLogger(this.getClass());	
	
	@Autowired
	private OrderService orderService;
	
	private String GZHID = ConfigBeanAdapter.gzhId;// 微信公众号id
	private String GZHSecret =ConfigBeanAdapter.gzhSecret;// 微信公众号密钥id
	private String SHHID = ConfigBeanAdapter.shhId;// 财付通商户号
	private String SHHKEY = ConfigBeanAdapter.shhKey;// 商户号对应的密钥	
	private String notify_url =ConfigBeanAdapter.notify_url;//这里notify_url是 微信处理完支付后的回调的应用系统接口url。
	
/**
 * 用户提交支付，获取微信支付订单接口
 */
@RequestMapping(value="/pay")
public ModelAndView pay(HttpServletRequest request,HttpServletResponse response){
	ModelAndView mv = new ModelAndView();
	Order order = new Order();
	/*------1.获取参数信息------- */
	//商户订单号
	String out_trade_no= request.getParameter("state"); 
	//价格
	String money = request.getParameter("money");
	//金额转化为分为单位
	String finalmoney = WeChat.getMoney(money);
	//获取用户的code
	String code = request.getParameter("code");
	
	if(Common.isEmpty(out_trade_no)){
		mv.addObject("ErrorMsg", "订单号不能为空");
		mv.setViewName("/order/error");
		return mv;
	}
	order = orderService.selectByPrimaryKey(out_trade_no);
	/*------2.根据code获取微信用户的openId和access_token------- */
	//注： 如果后台程序之前已经得到了用户的openId 可以不需要这一步，直接从存放openId的位置或session中获取就可以。
	//提交的url路径也就不需要再经过微信重定向。写成：http://localhost:8080/项目名/wechat/pay?money=${sumPrice}&state=${orderId}
	String openid=null;
	try {
		List<Object> list = accessToken(code);
		if(list.get(1) == null){
			openid =  (String) request.getSession().getAttribute("openid");
		}else{
			openid=list.get(1).toString();
			request.getSession().setAttribute("openid",openid);
		}
	} catch (IOException e) {
		logger.error("根据code获取微信用户的openId出现错误", e);
		mv.setViewName("/order/error");
		return mv;
	}
	
	/*------3.生成预支付订单需要的的package数据------- */
	//随机数 
	String nonce_str2= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
	//订单生成的机器 IP
	String spbill_create_ip = request.getRemoteAddr();
	//交易类型 ：jsapi代表微信公众号支付
	String trade_type = "JSAPI";
	
	SortedMap<String, String> packageParams = new TreeMap<String, String>();
	packageParams.put("appid",  GZHID);  
	packageParams.put("mch_id",  SHHID);  
	packageParams.put("nonce_str", nonce_str2);  
	packageParams.put("body", "费用");  
	packageParams.put("out_trade_no", out_trade_no);  
	packageParams.put("total_fee", finalmoney);  
	packageParams.put("spbill_create_ip", spbill_create_ip);  
	packageParams.put("notify_url", notify_url);  
	packageParams.put("trade_type", trade_type); 
	packageParams.put("openid", openid); 
	
	/*------4.根据package数据生成预支付订单号的签名sign------- */
	RequestHandler reqHandler = new RequestHandler(request, response);
	reqHandler.init( GZHID,  GZHSecret,  SHHKEY);

	String timestamp2 = String.valueOf(System.currentTimeMillis() / 1000); //生成签名时间戳
	String sign = reqHandler.createSign(packageParams);
	
	/*------5.生成需要提交给统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 的xml数据-------*/
	String xml="<xml>"+
			"<appid>"+ GZHID+"</appid>"+
			"<mch_id>"+ SHHID+"</mch_id>"+
			"<nonce_str>"+nonce_str2+"</nonce_str>"+
			"<sign>"+sign+"</sign>"+
			"<body><![CDATA["+"费用"+"]]></body>"+
			"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
			"<total_fee>"+finalmoney+"</total_fee>"+
			"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
			"<notify_url>"+notify_url+"</notify_url>"+
			"<trade_type>"+trade_type+"</trade_type>"+
			"<openid>"+openid+"</openid>"+
			"</xml>";
	
	/*------6.调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 生产预支付订单----------*/
	String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	String prepay_id="";
	try {
		prepay_id = GetWxOrderno.getPayNo(createOrderURL, xml);
		if(prepay_id.equals("")){
			mv.addObject("ErrorMsg", "支付错误");
			mv.setViewName("error");
			return mv;
		}
		
	} catch (Exception e) {
		logger.error("统一支付接口获取预支付订单出错", e); 
		mv.setViewName("/order/error");
		return mv;
	}


	order.setPayDeadline(DateUtil.getDateAfter(new Date(), 2)); //微信支付2小时之内付款  
	OrderPayment payment = new OrderPayment();
    payment.setOrderId(order.getOrderId());
	List<OrderPayment> list = orderService.selectByPayments(payment);
	payment = list.get(0);
    if(Common.isNotEmpty(openid))
       payment.setPaymentAccount(openid); //微信付款的opnenId
    payment.setPayCreate(new Date(Long.valueOf(timestamp2)*1000));
    payment.setPayName("微信公众号支付");
    payment.setPayType(Constants.OrderDetail.OrderPay.PayType.PAY_TYPEF);//支付类型 1:畅捷通快捷银联支付 2.畅捷通微信扫码支付 3：畅捷通支付宝扫码支付 4.畅捷通微信app支付 5：畅捷通支付宝app支付 6：微信公众号支付 7：其它支付

    if(Common.isNotEmpty(prepay_id))
    	payment.setTransactionId(prepay_id);
    if(Common.isNotEmpty(money))
        payment.setMoney(new BigDecimal(money));
    if(Common.isNotEmpty(spbill_create_ip))
        payment.setByerIp(spbill_create_ip);
    orderService.updateOrderPayment(payment);
	orderService.updateOrder(order);
	
	/*------7.将预支付订单的id和其他信息生成签名并一起返回到jsp页面 ------- */
	String nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
	SortedMap<String, String> finalpackage = new TreeMap<String, String>();
	String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
	String packages = "prepay_id="+prepay_id;
	finalpackage.put("appId",  GZHID);  
	finalpackage.put("timeStamp", timestamp);  
	finalpackage.put("nonceStr", nonce_str);  
	finalpackage.put("package", packages);  
	finalpackage.put("signType", "MD5");
	String finalsign = reqHandler.createSign(finalpackage);

	mv.addObject("signType", "MD5");
	mv.addObject("appid",  GZHID);
	mv.addObject("timeStamp2", timestamp2);
	mv.addObject("timeStamp", timestamp);
	mv.addObject("nonceStr2", nonce_str2);
	mv.addObject("nonceStr", nonce_str);
	mv.addObject("packageValue", packages);
	mv.addObject("paySign", finalsign);
	mv.addObject("sign", sign);
	mv.addObject("success","ok");
	mv.setViewName("/order/payOrder");
   return mv;
}
    
/**
 * 提交支付后的微信异步返回接口
 */
@RequestMapping(value="/weixinNotify")
public void weixinNotify(HttpServletRequest request, HttpServletResponse response){
	String out_trade_no=null;
	String return_code =null;
	try {
		InputStream inStream = request.getInputStream();
		ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while ((len = inStream.read(buffer)) != -1) {
			outSteam.write(buffer, 0, len);
		}
		outSteam.close();
		inStream.close();
		String resultStr  = new String(outSteam.toByteArray(),"utf-8");
		System.out.println("支付成功的回调："+resultStr);
		Map<String, Object> resultMap = parseXmlToList(resultStr);
		String result_code = (String) resultMap.get("result_code");
		String is_subscribe = (String) resultMap.get("is_subscribe");
		String transaction_id = (String) resultMap.get("transaction_id");
		String sign = (String) resultMap.get("sign");
		String time_end = (String) resultMap.get("time_end");
		String bank_type = (String) resultMap.get("bank_type");
		String err_code_des =(String) resultMap.get("err_code_des");
		
		out_trade_no = (String) resultMap.get("out_trade_no");
		return_code = (String) resultMap.get("return_code");
		
		Order order = new Order();
    	order.setOrderId(out_trade_no);
    	order = orderService.selectByPrimaryKey(out_trade_no);	    			
    	OrderPayment payment= new OrderPayment();
    	payment.setOrderId(out_trade_no);//商品订单号        	
    	List<OrderPayment> list = orderService.selectByPayments(payment);
    	
    	//2017年4月5日修改,由于当前系统只支持单店铺，不存在提交一个订单，生成多个支付详单的情况，默认一个订单生成一个支付详单
    	payment = list.get(0);
    	if(order !=null && payment != null){ 
	    	String order_payment_status_now = payment.getStatus();
	    	String order_pay_status_now = order.getPayStatus();
            //如果订单当前支付状态已经是支付完成,不再重复修改订单支付状态，通知畅捷通成功
			if( Constants.OrderDetail.OrderPay.PAY_STATUSA.equals(order_payment_status_now) && Constants.OrderDetail.OrderPay.PAY_STATUSA.equals(order_pay_status_now)){
				response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
			}
            payment.setPayClose(DateUtil.dateStrToDate(time_end)); //微信只提供了这个交易完成时间
        }
         if("SUCCESS".equals(result_code)){ //支付成功
        	payment.setModifyDate(new Date());
            payment.setStatus(Constants.OrderDetail.OrderPay.PAY_STATUSA);//付款状态
            payment.setTransactionId(transaction_id);
            order.setStatus(Constants.OrderDetail.OrderStatus.ORDER_STATUSD);
			order.setPayStatus(Constants.OrderDetail.OrderPay.PAY_STATUSA);
            int insertPaymentResult =  orderService.updateOrderPayment(payment);
            int insertOrderResult = orderService.updateOrder(order);
            if(insertPaymentResult > 0 && insertOrderResult > 0){
            	if("false".equals(ConfigBeanAdapter.isEnable_quartz_start)){ //读取配置文件配置标识
	            	final Order order_ = order;
	            	//将订单上传到T+系统中
	            	new Thread(new Runnable() {
						
						@Override
						public void run() {
							orderService.addOrderToTSaleOrder(order_);
						}
					}).start();
				 }
				//通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
				response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
		    	
            }
		}else{ //支付失败
			payment.setModifyDate(new Date());
            payment.setStatus(Constants.OrderDetail.OrderPay.PAY_STATUSB);//付款状态
            payment.setMemo(err_code_des);;
			order.setPayStatus(Constants.OrderDetail.OrderPay.PAY_STATUSB);
            orderService.updateOrderPayment(payment);
            orderService.updateOrder(order);
		}
		request.setAttribute("out_trade_no", out_trade_no);
		//通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
		response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
	}  catch (Exception e) {
		logger.error("微信回调接口出现错误：",e);
		try {
			response.getWriter().write(RequestHandler.setXML("FAIL", "error"));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	} 
}
    
    /**
     *微信公众号申请退款
     * @param orderId 订单id
     * @param total_fee 退款金额
     * @param refund_fee 退款金额
     * @param response
     * @return
     * @throws Exception 
     */
    @RequestMapping(value="/refundWXPay")
    @ResponseBody
    private ModelAndView refundwx(HttpServletRequest request,HttpServletResponse response) throws Exception{
        System.out.print("退款");
		ModelAndView mv =new ModelAndView();
    	
    	/*--------1.初始化数据参数----------*/
    	String appId= "wxfd7c065eee11112222";
    	String secret= "b5b3a627f5d1f8888888888888";
    	String shh= "111111111";
    	String key= "mmmmmmmmmmmmmmm";
    	String filePath = "E:\\证书\\apiclient_cert.p12"; //退款需要提供证书数据，所以需要根据证书路径读取证书
    	//需要退款的商户订单号，对应提交订单中的out_trade_no
    	String orderId = request.getParameter("orderId").toString();
    	String total_fee=WeChat.getMoney(request.getParameter("total_fee"));  //也可以根据业务场景从数据库中获取总金额和退款金额
    	String refund_fee=WeChat.getMoney(request.getParameter("refund_fee"));
    	Map<String,String> result = (Map<String, String>) wxRefund(request,response,appId,secret,shh,key,orderId,total_fee,refund_fee,filePath);
    	
    	/*
		根据result的返回状态，处理你的业务逻辑
		.....
		*/
		
		mv.setViewName("wechat/refundFind");
		return mv;
    }
    
    private Object wxRefund(HttpServletRequest request,HttpServletResponse response,String appId,
    					String secret,String shh,String key,String orderId,String total_fee,String refund_fee,String path){
    	Map<String,String> result=new HashMap<String,String>(); 
    	String refundid = Common.getUUID();
		String nonce_str = MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
		
		/*----------  1.生成预支付订单需要的的package数据-----------*/
		SortedMap<String, String> packageParams = new TreeMap<String, String>();
		packageParams.put("appid", appId);  
		packageParams.put("mch_id", shh);  
		packageParams.put("nonce_str", nonce_str);  
		packageParams.put("op_user_id", shh);  
		packageParams.put("out_trade_no", orderId);  
		packageParams.put("out_refund_no", refundid);  
		packageParams.put("total_fee",total_fee);  
		packageParams.put("refund_fee", refund_fee);  
		/*------2.根据package生成签名sign------- */
		RequestHandler reqHandler = new RequestHandler(request, response);
		reqHandler.init(appId, secret, key);
		String sign = reqHandler.createSign(packageParams);
		
		/*------3.拼装需要提交到微信的数据xml------- */
    	String xml="<xml>"
        +"<appid>"+appId+"</appid>"
        + "<mch_id>"+shh+"</mch_id>"
        + "<nonce_str>"+nonce_str+"</nonce_str>"
        + "<op_user_id>"+shh+"</op_user_id>"
        + "<out_trade_no>"+orderId+"</out_trade_no>"
        + "<out_refund_no>"+refundid+"</out_refund_no>"
        + "<refund_fee>"+refund_fee+"</refund_fee>"
        + "<total_fee>"+total_fee+"</total_fee>"
        + "<sign>"+sign+"</sign>"
        +"</xml>";
	     try {
	    	 /*--------4.读取证书文件,这一段是直接从微信支付平台提供的demo中copy的，所以一般不需要修改------*/
	    	 KeyStore keyStore  = KeyStore.getInstance("PKCS12");
	    	 FileInputStream instream = new FileInputStream(new File(path));
	    	 try {
	    		 keyStore.load(instream, shh.toCharArray());
	    	 } finally {
	    		 instream.close();
	    	 }
	    	 // Trust own CA and all self-signed certs
	    	 SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, shh.toCharArray()).build();
	    	 // Allow TLSv1 protocol only
	    	 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,
	    			 SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
	    	 CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
	    	 
	    	 /*------5.发送数据到微信的退款接口------- */
	    	 String url="https://api.mch.weixin.qq.com/secapi/pay/refund";
	    	 HttpPost httpost= HttpClientConnectionManager.getPostMethod(url);
			 httpost.setEntity(new StringEntity(xml, "UTF-8"));
			 HttpResponse weixinResponse = httpClient.execute(httpost);
		     String jsonStr = EntityUtils.toString(weixinResponse.getEntity(), "UTF-8");
		     System.out.println(jsonStr);
		     
		     Map map = GetWxOrderno.doXMLParse(jsonStr);
		     if("success".equalsIgnoreCase((String) map.get("return_code"))){
		    	 System.out.println("退款成功");
		    	 result.put("returncode", "ok");
		    	 result.put("returninfo", "退款成功");
		     }else{
		    	 System.out.println("退款失败");
		    	 result.put("returncode", "error");
		    	 result.put("returninfo", "退款失败");
		     }
		} catch (Exception e) {
			System.out.println("退款失败");
			result.put("returncode", "error");
		    result.put("returninfo", "退款失败");
		}
		return result;
    	
    }
    
    /**
	 * 通过微信用户的code换取网页授权access_token
	 * @return
	 * @throws IOException
	 * @throws
	 */
	public List<Object> accessToken(String code) throws IOException {
		List<Object> list = new ArrayList<Object>();
		String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+GZHID+ "&secret="+GZHSecret+"&code=" + code + "&grant_type=authorization_code";
		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(url);
		HttpResponse res = client.execute(post);
		if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
			HttpEntity entity = res.getEntity();
			String str = EntityUtils.toString(entity, "utf-8");
			ObjectMapper mapper=new ObjectMapper();
			Map<String,Object> jsonOb=mapper.readValue(str, Map.class);
			list.add(jsonOb.get("access_token"));
			list.add(jsonOb.get("openid"));
		}
		return list;
	}
    
	/**
	 * description: 解析微信通知xml
	 * 
	 * @param xml
	 * @return
	 * @author ex_yangxiaoyi
	 * @see
	 */
	@SuppressWarnings({ "unused", "rawtypes", "unchecked" })
	private static Map parseXmlToList(String xml) {
		Map retMap = new HashMap();
		try {
			StringReader read = new StringReader(xml);
			// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
			InputSource source = new InputSource(read);
			// 创建一个新的SAXBuilder
			SAXBuilder sb = new SAXBuilder();
			// 通过输入源构造一个Document
			Document doc = (Document) sb.build(source);
			Element root = doc.getRootElement();// 指向根节点
			List<Element> es = root.getChildren();
			if (es != null && es.size() != 0) {
				for (Element element : es) {
					retMap.put(element.getName(), element.getValue());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retMap;
	}

}